#ifndef A8_JSON_H
#define A8_JSON_H

#include <a8/xvalue.hpp>

namespace a8
{
	static std::string json_escapestring(const char* s)
	{
		std::string result;
		result.reserve(strlen(s) * 2);
		const char* p = s;
		while(*p){
			if(*p >=0 && *p < ' '){
				switch(*p){
				case '\b':
					result.append("\\b");
					break;
				case '\t':
					result.append("\\t");
					break;
				case '\n':
					result.append("\\n");
					break;
				case '\f':
					result.append("\\f");
					break;
				case '\r':
					result.append("\\r");
					break;
				default:
					{
						char unicodehex[6] = {'\\', 'u', '0', '0', '0', '0'}; 
						char *pu = &unicodehex[5];
						int n = *p;
						do{
							*pu-- = "0123456789abcdef"[n % 16];
							n /= 16;
						}while(n);
						result.append(unicodehex);
					}
				}
			}else if (*p == '"'){
				result.append("\\\"");
			}else if (*p == '\\'){
				result.append("\\\\");

			}else{
				result.push_back(*p);
			}//end if
			p++;
		}
		return result;
	}

	static void begin_jsonresponse(std::string& jsonstr)
	{
		jsonstr.push_back('{');
	}

	static void end_jsonresponse(std::string& jsonstr)
	{
		if(jsonstr[jsonstr.length() - 1] == ','){
			jsonstr[jsonstr.length() - 1] = '}';
		}else{
			jsonstr.push_back('}');
		}
	}

	static void json_push(std::string& jsonstr, const char* val)
	{
		jsonstr.append(json_escapestring(val));
		jsonstr.push_back(',');
	}

	static void json_push(std::string& jsonstr, const std::string& val)
	{
		json_push(jsonstr, val.c_str());
	}

	static void json_push(std::string& jsonstr, const char val)
	{
		json_push(jsonstr, std::string(1, val));
	}

	static void json_push(std::string& jsonstr, int val)
	{
		jsonstr.append(a8::XValue(val).GetString().c_str());
		jsonstr.push_back(',');
	}

	static void json_push(std::string& jsonstr, unsigned int val)
	{
		jsonstr.append(a8::XValue((unsigned long long)val).GetString().c_str());
		jsonstr.push_back(',');
	}

	static void json_push(std::string& jsonstr, double val)
	{
		jsonstr.append(a8::XValue(val).GetString().c_str());
		jsonstr.push_back(',');
	}

	static void json_pushobject(std::string& jsonstr, const char* key)
	{
		jsonstr.push_back('"');
		jsonstr.append(json_escapestring(key));
		jsonstr.append("\":{");
	}

	static void json_pusharray(std::string& jsonstr, const char* key)
	{
		jsonstr.push_back('"'); 
		jsonstr.append(json_escapestring(key));
		jsonstr.append("\":[");
	}

	static void json_pushobject(std::string& jsonstr)
	{
		jsonstr.push_back('{');
	}

	static void json_pusharray(std::string& jsonstr)
	{
		jsonstr.push_back('[');
	}

	static void json_endobject(std::string& jsonstr)
	{
		if(jsonstr[jsonstr.length() - 1] == ','){
			jsonstr[jsonstr.length() - 1] = '}';
		} else {
			jsonstr.push_back('}');
		}
		jsonstr.push_back(',');
	}

	static void json_endarray(std::string& jsonstr)
	{
		if(jsonstr[jsonstr.length() - 1] == ','){
			jsonstr[jsonstr.length() - 1] = ']';
		} else {
			jsonstr.push_back(']');
		}
		jsonstr.push_back(',');
	}

	template<unsigned int N>
	static void json_push(std::string& jsonstr, const char* key, char(&val)[N])
	{
		for(int i = N - 1; i >= 0; i--){
			if(val[i] == '\0'){ //is \0 terminated
				json_push(jsonstr, (const char*) val);
				return;
			}
		}
		char* p = (char*) malloc(N + 1);
		memmove(p, val, N);
		p[N] = '\0';
		json_push(jsonstr,(const char*) val);
		free(p);
	}

	template<typename _T_>
	static void json_push(std::string& jsonstr, const _T_& val)
	{
		json_pushobject(jsonstr);
		val.encode_to_json(jsonstr);
		json_endobject(jsonstr);
		jsonstr.push_back(',');
	}

	static void json_pushpair(std::string& jsonstr, const char* key, const char* val)
	{
		jsonstr.push_back('"');
		jsonstr.append(json_escapestring(key));
        jsonstr.append("\":\"");
        jsonstr.append(json_escapestring(val));
        jsonstr.append("\",");
	}

	static void json_pushpair_with_noquote(std::string& jsonstr, const char* key, const char* val)
    {
		jsonstr.push_back('"');
		jsonstr.append(json_escapestring(key));
        jsonstr.append("\":");
        jsonstr.append(json_escapestring(val));
        jsonstr.append(",");
    }

	static void json_pushpair(std::string& jsonstr, const char* key, const std::string& val)
	{
		json_pushpair(jsonstr, key, val.c_str());
	}

	static void json_pushpair(std::string& jsonstr, const char* key, const char val)
	{
		json_pushpair(jsonstr, key, std::string(1, val));
	}

	static void json_pushpair(std::string& jsonstr, const char* key, int val)
	{
		json_pushpair_with_noquote(jsonstr, key, a8::XValue((long long)val).GetString().c_str());
	}

	static void json_pushpair(std::string& jsonstr, const char* key, unsigned int val)
	{
		json_pushpair_with_noquote(jsonstr, key, a8::XValue((unsigned long long)val).GetString().c_str());
	}

	static void json_pushpair(std::string& jsonstr, const char* key, long long val)
	{
		json_pushpair(jsonstr, key, a8::XValue((long long)val).GetString().c_str());
	}

	static void json_pushpair(std::string& jsonstr, const char* key, unsigned long long val)
	{
		json_pushpair(jsonstr, key, a8::XValue((unsigned long long)val).GetString().c_str());
	}

	static void json_pushpair(std::string& jsonstr, const char* key, double val)
	{
		json_pushpair_with_noquote(jsonstr, key, a8::XValue(val).GetString().c_str());
	}

	template<unsigned int N>
	static void json_pushpair(std::string& jsonstr, const char* key, char(&val)[N])
	{
		for(int i = N - 1; i >= 0; i--){
			if(val[i] == '\0'){ //is \0 terminated
				json_pushpair(jsonstr, key, (const char*) val);
				return;
			}
		}
		char* p = (char*) malloc(N + 1);
		memmove(p, val, N);
		p[N] = '\0';
		json_pushpair(jsonstr, key, (const char*) val);
		free(p);
	}

	template<typename _T_>
	static void json_pushpair(std::string& jsonstr, const char* key, const _T_& val)
	{
		jsonstr.push_back('"');
		jsonstr.append(json_escapestring(key));
		jsonstr.append("\":");
		json_pushobject(jsonstr);
		val.encode_to_json(jsonstr);
		json_endobject(jsonstr);
	}

	static void json_pushpair(std::string& jsonstr, const char* key, const std::vector<int>& vals)
	{
		json_pusharray(jsonstr, key);
		for(int i = 0; i < vals.size(); i++){
			json_push(jsonstr, vals[i]);
		}
		json_endarray(jsonstr);
	}

	static void json_pushpair(std::string& jsonstr, const char* key, const std::vector<unsigned int>& vals)
	{
		json_pusharray(jsonstr, key);
		for(int i = 0; i < vals.size(); i++){
			json_push(jsonstr, vals[i]);
		}
		json_endarray(jsonstr);
	}

	static void json_pushpair(std::string& jsonstr, const char* key, const std::vector<double>& vals)
	{
		json_pusharray(jsonstr, key);
		for(int i = 0; i < vals.size(); i++){
			json_push(jsonstr, vals[i]);
		}
		json_endarray(jsonstr);
	}

	static void json_pushpair(std::string& jsonstr, const char* key, const std::vector<std::string>& vals)
	{
		json_pusharray(jsonstr, key);
		for(int i = 0; i < vals.size(); i++){
			json_push(jsonstr, vals[i]);
		}
		json_endarray(jsonstr);
	}

	template<typename _T_>
	static void json_pushpair(std::string& jsonstr, const char* key, const std::vector<_T_>& vals)
	{
		json_pusharray(jsonstr, key);
		for(int i = 0; i < vals.size(); i++){
			json_pushobject(jsonstr);
			vals[i].encode_to_json(jsonstr);
			json_endobject(jsonstr);
		}
		json_endarray(jsonstr);
	}

}

#endif
